by Carsten Rieger · Published · Updated
Following this guide you will be able to install and configure Nextcloud 18 latest based on Debian 9.x Stretch or Debian 10.x Buster, NGINX 1.17.x, TLSv1.3, PHP 7.3, MariaDB 10.4, Redis, fail2ban, firewall (ufw) and will achieve an A+ rating from both, Nextcloud and Qualys SSL Labs. We will request and implement your ssl certificate(s) from Let’s Encrypt. You only have to ammend the red marked values (YOUR.DEDYN.IO, 192.168.2.x, ssh port 22) regarding your environment!
the entire update history
From my perspective the requirements for this guide may be rated as low: you only have to
•.provide a 64Bit Server (e.g. Intel NUC),
•.forward two ports (80 and 443) from internet (your router e.g. FritzBox or Speedport) to your internal Nextcloud server
•.and install the operating system Debian Stretch 9.x (64Bit).
Prepare your server for the installation itself:
su -
(or sudo -s)
apt install curl gnupg2 git lsb-release ssl-cert ca-certificates apt-transport-https tree locate software-properties-common dirmngr screen htop net-tools zip unzip curl ffmpeg ghostscript libfile-fcntllock-perl -y
Add new software repositories
cd /etc/apt/sources.list.d
echo "deb [arch=amd64] http://nginx.org/packages/mainline/debian $(lsb_release -cs) nginx" | tee nginx.list
echo "deb [arch=amd64] https://packages.sury.org/php/ $(lsb_release -cs) main" | tee php.list
echo "deb [arch=amd64] http://mirror2.hs-esslingen.de/mariadb/repo/10.4/debian $(lsb_release -cs) main" | tee mariadb.list
Download the required keys to trust all the new sources:
curl -fsSL https://nginx.org/keys/nginx_signing.key | sudo apt-key add -
wget -q https://packages.sury.org/php/apt.gpg -O- | apt-key add -
apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xF1656F24C74CD1D8
Update your server, generate self signed certificates and install nginx:
apt update && apt upgrade -y && apt install ssl-cert -y && make-ssl-cert generate-default-snakeoil
apt remove nginx nginx-extras nginx-common nginx-full -y --allow-change-held-packages
Ensure Apache(2) isn’t running otherwise NGINX won’t start because the required port (:80) would be in use by Apache(2):
systemctl stop apache2.service && systemctl disable apache2.service
apt install nginx -y && systemctl enable nginx.service
mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak && vi /etc/nginx/nginx.conf
Paste the following rows:
user www-data;
worker_processes auto;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
multi_accept on;
use epoll;
}
http {
server_names_hash_bucket_size 64;
upstream php-handler {
server unix:/run/php/php7.3-fpm.sock;
}
set_real_ip_from 127.0.0.1;
set_real_ip_from 192.168.2.0/24;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
include /etc/nginx/mime.types;
#include /etc/nginx/proxy.conf;
#include /etc/nginx/ssl.conf;
#include /etc/nginx/header.conf;
#include /etc/nginx/optimization.conf;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log warn;
sendfile on;
send_timeout 3600;
tcp_nopush on;
tcp_nodelay on;
open_file_cache max=500 inactive=10m;
open_file_cache_errors on;
keepalive_timeout 65;
reset_timedout_connection on;
server_tokens off;
resolver 192.168.2.1 valid=30s;
#resolver 127.0.0.53 valid=30s; is recommended but reuqires a valid resolver configuration
resolver_timeout 5s;
include /etc/nginx/conf.d/*.conf;
}
service nginx restart
mkdir -p /var/nc_data /var/www/letsencrypt
chown -R www-data:www-data /var/nc_data /var/www
apt install php7.3-fpm php7.3-gd php7.3-mysql php7.3-curl php7.3-xml php7.3-zip php7.3-intl php7.3-mbstring php7.3-json php7.3-bz2 php7.3-ldap php-apcu imagemagick php-imagick -y
Awesome, PHP 7.3 is already installed. Verify your timezone settings
date
and if necessary set it properly
timedatectl set-timezone Europe/Berlin
cp /etc/php/7.3/fpm/pool.d/www.conf /etc/php/7.3/fpm/pool.d/www.conf.bak
cp /etc/php/7.3/cli/php.ini /etc/php/7.3/cli/php.ini.bak
cp /etc/php/7.3/fpm/php.ini /etc/php/7.3/fpm/php.ini.bak
cp /etc/php/7.3/fpm/php-fpm.conf /etc/php/7.3/fpm/php-fpm.conf.bak
sed -i "s/;env\[HOSTNAME\] = /env[HOSTNAME] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/;env\[TMP\] = /env[TMP] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/;env\[TMPDIR\] = /env[TMPDIR] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/;env\[TEMP\] = /env[TEMP] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/;env\[PATH\] = /env[PATH] = /" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/pm.max_children = .*/pm.max_children = 240/" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/pm.start_servers = .*/pm.start_servers = 20/" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/pm.min_spare_servers = .*/pm.min_spare_servers = 10/" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/pm.max_spare_servers = .*/pm.max_spare_servers = 20/" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/;pm.max_requests = 500/pm.max_requests = 500/" /etc/php/7.3/fpm/pool.d/www.conf
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.3/cli/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 1800/" /etc/php/7.3/cli/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.3/cli/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.3/cli/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.3/cli/php.ini
sed -i "s/max_file_uploads =.*/max_file_uploads = 100/" /etc/php/7.3/cli/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.3/cli/php.ini
sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/7.3/cli/php.ini
sed -i "s/memory_limit = 128M/memory_limit = 512M/" /etc/php/7.3/fpm/php.ini
sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/7.3/fpm/php.ini
sed -i "s/max_execution_time =.*/max_execution_time = 1800/" /etc/php/7.3/fpm/php.ini
sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/7.3/fpm/php.ini
sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/7.3/fpm/php.ini
sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/7.3/fpm/php.ini
sed -i "s/max_file_uploads =.*/max_file_uploads = 100/" /etc/php/7.3/fpm/php.ini
sed -i "s/;date.timezone.*/date.timezone = Europe\/\Berlin/" /etc/php/7.3/fpm/php.ini
sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.enable=.*/opcache.enable=1/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.enable_cli=.*/opcache.enable_cli=1/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.memory_consumption=.*/opcache.memory_consumption=128/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.interned_strings_buffer=.*/opcache.interned_strings_buffer=8/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=10000/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.revalidate_freq=.*/opcache.revalidate_freq=1/" /etc/php/7.3/fpm/php.ini
sed -i "s/;opcache.save_comments=.*/opcache.save_comments=1/" /etc/php/7.3/fpm/php.ini
sed -i '$aapc.enable_cli=1' /etc/php/7.3/mods-available/apcu.ini
sed -i "s/;emergency_restart_threshold =.*/emergency_restart_threshold = 10/" /etc/php/7.3/fpm/php-fpm.conf
sed -i "s/;emergency_restart_interval =.*/emergency_restart_interval = 1m/" /etc/php/7.3/fpm/php-fpm.conf
sed -i "s/;process_control_timeout =.*/process_control_timeout = 10s/" /etc/php/7.3/fpm/php-fpm.conf
sed -i "s/09,39.*/# &/" /etc/cron.d/php
(crontab -l ; echo "09,39 * * * * /usr/lib/php/sessionclean 2>&1") | crontab -u root -
cp /etc/ImageMagick-6/policy.xml /etc/ImageMagick-6/policy.xml.bak
sed -i "s/rights\=\"none\" pattern\=\"PS\"/rights\=\"read\|write\" pattern\=\"PS\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights\=\"none\" pattern\=\"EPI\"/rights\=\"read\|write\" pattern\=\"EPI\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights\=\"none\" pattern\=\"PDF\"/rights\=\"read\|write\" pattern\=\"PDF\"/" /etc/ImageMagick-6/policy.xml
sed -i "s/rights\=\"none\" pattern\=\"XPS\"/rights\=\"read\|write\" pattern\=\"XPS\"/" /etc/ImageMagick-6/policy.xml
service php7.3-fpm restart && service nginx restart
apt update && apt install mariadb-server -y
Verify your database server version:
mysql --version
mysql Ver 15.1 Distrib 10.4.x-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2 should appear.
mysql_secure_installation
Switch to unix_socket authentication [Y/n] N
Enter current password for root (enter for none): <ENTER> or type the password Set root password? [Y/n] Y
If already set during the MariaDB installation you will be asked wether to change or keep the password
Remove anonymous users? [Y/n] Y
Disallow root login remotely? [Y/n] Y
Remove test database and access to it? [Y/n] Y
Reload privilege tables now? [Y/n] Y
service mysql stop
mv /etc/mysql/my.cnf /etc/mysql/my.cnf.bak && vi /etc/mysql/my.cnf
Paste the following rows:
[client]
default-character-set = utf8mb4
port = 3306
socket = /var/run/mysqld/mysqld.sock
[mysqld_safe]
log_error = /var/log/mysql/mysql_error.log
nice = 0
socket = /var/run/mysqld/mysqld.sock
[mysqld]
basedir = /usr
bind-address = 127.0.0.1
binlog_format = ROW
bulk_insert_buffer_size = 16M
character-set-server = utf8mb4
collation-server = utf8mb4_general_ci
concurrent_insert = 2
connect_timeout = 5
datadir = /var/lib/mysql
default_storage_engine = InnoDB
expire_logs_days = 7
general_log_file = /var/log/mysql/mysql.log
general_log = 0
innodb_buffer_pool_size = 1024M
innodb_buffer_pool_instances = 1
innodb_flush_log_at_trx_commit = 2
innodb_log_buffer_size = 32M
innodb_max_dirty_pages_pct = 90
innodb_file_per_table = 1
innodb_open_files = 400
innodb_io_capacity = 4000
innodb_flush_method = O_DIRECT
key_buffer_size = 128M
lc_messages_dir = /usr/share/mysql
lc_messages = en_US
log_bin = /var/log/mysql/mariadb-bin
log_bin_index = /var/log/mysql/mariadb-bin.index
log_error=/var/log/mysql/mysql_error.log
log_slow_verbosity = query_plan
log_warnings = 2
long_query_time = 1
max_allowed_packet = 16M
max_binlog_size = 100M
max_connections = 200
max_heap_table_size = 64M
myisam_recover_options = BACKUP
myisam_sort_buffer_size = 512M
port = 3306
pid-file = /var/run/mysqld/mysqld.pid
query_cache_limit = 2M
query_cache_size = 64M
query_cache_type = 1
query_cache_min_res_unit = 2k
read_buffer_size = 2M
read_rnd_buffer_size = 1M
skip-external-locking
skip-name-resolve
slow_query_log_file = /var/log/mysql/mariadb-slow.log
slow-query-log = 1
socket = /var/run/mysqld/mysqld.sock
sort_buffer_size = 4M
table_open_cache = 400
thread_cache_size = 128
tmp_table_size = 64M
tmpdir = /tmp
transaction_isolation = READ-COMMITTED
user = mysql
wait_timeout = 600
[mysqldump]
max_allowed_packet = 16M
quick
quote-names
[isamchk]
key_buffer = 16M
service mysql restart && mysql -uroot -p
•.the database nextcloud
•.the user nextcloud
•.and its password
CREATE DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER nextcloud@localhost identified by 'nextcloud'; GRANT ALL PRIVILEGES on nextcloud.* to nextcloud@localhost; FLUSH privileges; quit;
Verify the transaction Isolation level was set to READ_Commit and the collation was set to UTF8MB4 properly:
mysql -h localhost -uroot -p -e "SELECT @@TX_ISOLATION; SELECT SCHEMA_NAME 'database', default_character_set_name 'charset', DEFAULT_COLLATION_NAME 'collation' FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='nextcloud'"
If the resultset will be “READ-COMMITTED” and “utf8mb4_general_ci” as shown go ahead with the installation of redis.
apt update && apt install redis-server php-redis -y
cp /etc/redis/redis.conf /etc/redis/redis.conf.bak
sed -i "s/port 6379/port 0/" /etc/redis/redis.conf
sed -i s/\#\ unixsocket/\unixsocket/g /etc/redis/redis.conf
sed -i "s/unixsocketperm 700/unixsocketperm 770/" /etc/redis/redis.conf
sed -i "s/# maxclients 10000/maxclients 512/" /etc/redis/redis.conf
usermod -a -G redis www-data
cp /etc/sysctl.conf /etc/sysctl.conf.bak
sed -i '$avm.overcommit_memory = 1' /etc/sysctl.conf
It is recommended to restart your server once:
shutdown -r now
Create the configuration files, start with /etc/nginx/conf.d/nextcloud.conf
su -
[ -f /etc/nginx/conf.d/default.conf ] && mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bak
touch /etc/nginx/conf.d/default.conf
vi /etc/nginx/conf.d/nextcloud.conf
Paste the following rows:
server {
server_name your.dedyn.io;
listen 80 default_server;
listen [::]:80 default_server;
location ^~ /.well-known/acme-challenge {
proxy_pass http://127.0.0.1:81;
proxy_set_header Host $host;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
server_name your.dedyn.io;
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
root /var/www/nextcloud/;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /.well-known/carddav {
return 301 $scheme://$host/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/remote.php/dav;
}
#SOCIAL app enabled? Please uncomment the following row
#rewrite ^/.well-known/webfinger /public.php?service=webfinger last;
#WEBFINGER app enabled? Please uncomment the following two rows.
#rewrite ^/.well-known/host-meta /public.php?service=host-meta last;
#rewrite ^/.well-known/host-meta.json /public.php?service=host-meta-json last;
client_max_body_size 10240M;
location / {
rewrite ^ /index.php;
}
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
}
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ^~ /apps/rainloop/app/data {
deny all;
}
location ~ \.(?:flv|mp4|mov|m4a)$ {
mp4;
mp4_buffer_size 100M;
mp4_max_buffer_size 1024M;
fastcgi_split_path_info ^(.+?.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
include php_optimization.conf;
}
location ~ ^\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
include php_optimization.conf;
}
location ~ ^\/(?:updater|oc[ms]-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}
location ~ \.(?:css|js|woff2?|svg|gif|map|png|html|ttf|ico|jpg|jpeg)$ {
try_files $uri /index.php$request_uri;
access_log off;
expires 360d;
}
}
If you want your Nextcloud running in a subdirectory (subfolder) like https://your.dedyn.io/nextcloud use this nextcloud.conf instead:
server {
server_name your.dedyn.io;
listen 80 default_server;
listen [::]:80 default_server;
location ^~ /.well-known/acme-challenge {
proxy_pass http://127.0.0.1:81;
proxy_set_header Host $host;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
server_name your.dedyn.io;
listen 443 ssl http2 default_server;
listen [::]:443 ssl http2 default_server;
root /var/www/;
location = /robots.txt {
allow all;
log_not_found off;
access_log off;
}
location = /.well-known/carddav {
return 301 $scheme://$host/nextcloud/remote.php/dav;
}
location = /.well-known/caldav {
return 301 $scheme://$host/nextcloud/remote.php/dav;
}
location = /.well-known/webfinger {
return 301 $scheme://$host/nextcloud/public.php?service=webfinger;
}
client_max_body_size 10240M;
location /nextcloud {
rewrite ^ /nextcloud/index.php;
}
location ~ ^\/nextcloud\/(?:build|tests|config|lib|3rdparty|templates|data)\/ {
deny all;
}
location ~ ^\/nextcloud\/(?:\.|autotest|occ|issue|indie|db_|console) {
deny all;
}
location ^~ /apps/rainloop/app/data {
deny all;
}
location ~ \.(?:flv|mp4|mov|m4a)$ {
mp4;
mp4_buffer_size 100M;
mp4_max_buffer_size 1024M;
fastcgi_split_path_info ^(.+?.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
include php_optimization.conf;
}
location ~ ^\/nextcloud\/(?:index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|oc[ms]-provider\/.+)\.php(?:$|\/) {
fastcgi_split_path_info ^(.+?\.php)(\/.*|)$;
set $path_info $fastcgi_path_info;
try_files $fastcgi_script_name =404;
include fastcgi_params;
include /etc/nginx/php_optimization.conf;
}
location ~ ^\/nextcloud\/(?:updater|oc[ms]-provider)(?:$|\/) {
try_files $uri/ =404;
index index.php;
}
location ~ ^\/nextcloud\/.+[^\/]\.(?:css|js|woff2?|svg|gif|map|png|html|ttf|ico|jpg|jpeg)$ {
try_files $uri /nextcloud/index.php$request_uri;
access_log off;
expires 360d;
}
}
vi /etc/nginx/conf.d/letsencrypt.conf
Paste the following rows:
server {
server_name 127.0.0.1;
listen 127.0.0.1:81 default_server;
charset utf-8;
location ^~ /.well-known/acme-challenge {
default_type text/plain;
root /var/www/letsencrypt;
}
}
vi /etc/nginx/ssl.conf
Paste the following rows
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
ssl_trusted_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
#ssl_certificate /etc/letsencrypt/rsa-certs/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/rsa-certs/privkey.pem;
#ssl_certificate /etc/letsencrypt/ecc-certs/fullchain.pem;
#ssl_certificate_key /etc/letsencrypt/ecc-certs/privkey.pem;
#ssl_trusted_certificate /etc/letsencrypt/ecc-certs/chain.pem;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.3 TLSv1.2;
ssl_ciphers 'TLS-CHACHA20-POLY1305-SHA256:TLS-AES-256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384';
ssl_ecdh_curve X448:secp521r1:secp384r1:prime256v1;
ssl_prefer_server_ciphers on;
ssl_stapling on;
ssl_stapling_verify on;
vi /etc/nginx/proxy.conf
Paste the following rows
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_connect_timeout 3600;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
proxy_redirect off;
vi /etc/nginx/header.conf
Paste the following rows
add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;";
add_header X-Robots-Tag none;
add_header X-Download-Options noopen;
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer" always;
#add_header Feature-Policy "accelerometer 'none'; autoplay 'self'; geolocation 'none'; midi 'none'; sync-xhr 'self' ; microphone 'self'; camera 'self'; magnetometer 'none'; gyroscope 'none'; speaker 'self'; fullscreen 'self'; payment 'none'; usb 'none'";
add_header X-Frame-Options "SAMEORIGIN";
vi /etc/nginx/optimization.conf
Paste the following rows
fastcgi_hide_header X-Powered-By;
fastcgi_read_timeout 3600;
fastcgi_send_timeout 3600;
fastcgi_connect_timeout 3600;
fastcgi_buffers 64 64K;
fastcgi_buffer_size 256k;
fastcgi_busy_buffers_size 3840K;
fastcgi_cache_key $http_cookie$request_method$host$request_uri;
fastcgi_cache_use_stale error timeout invalid_header http_500;
fastcgi_ignore_headers Cache-Control Expires Set-Cookie;
gzip on;
gzip_vary on;
gzip_comp_level 4;
gzip_min_length 256;
gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;
gzip_disable "MSIE [1-6]\.";
vi /etc/nginx/php_optimization.conf
Paste the following rows
fastcgi_pass php-handler;
fastcgi_param HTTPS on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $path_info;
fastcgi_param HTTPS on;
fastcgi_param modHeadersAvailable true;
fastcgi_param front_controller_active true;
fastcgi_intercept_errors on;
fastcgi_request_buffering off;
fastcgi_cache_valid 404 1m;
fastcgi_cache_valid any 1h;
fastcgi_cache_methods GET HEAD;
openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
Please be patient, it will take a while depending on your hardware.
sed -i s/\#\include/\include/g /etc/nginx/nginx.conf && service nginx restart
cd /usr/local/src
wget https://download.nextcloud.com/server/releases/latest.tar.bz2
tar -xjf latest.tar.bz2 -C /var/www && chown -R www-data:www-data /var/www/ && rm -f latest.tar.bz2
Create a technical user to install and renew your ssl certificates
adduser acmeuser
usermod -a -G www-data acmeuser
Issue visudo
visudo
and add the the following row at the end of the file
acmeuser ALL=NOPASSWD: /bin/systemctl reload nginx.service
to e.g. restart nginx without a password.
su - acmeuser
curl https://get.acme.sh | sh
exit
Create three folders to request and store your ssl certificates to (substitute your.dedyn.io):
sudo -s
mkdir -p /var/www/letsencrypt/.well-known/acme-challenge /etc/letsencrypt/rsa-certs /etc/letsencrypt/ecc-certs
chmod -R 775 /var/www/letsencrypt /etc/letsencrypt && chown -R www-data:www-data /var/www/ /etc/letsencrypt
su - acmeuser
acme.sh --issue -d your.dedyn.io --keylength 4096 -w /var/www/letsencrypt --key-file /etc/letsencrypt/rsa-certs/privkey.pem --ca-file /etc/letsencrypt/rsa-certs/chain.pem --cert-file /etc/letsencrypt/rsa-certs/cert.pem --fullchain-file /etc/letsencrypt/rsa-certs/fullchain.pem
acme.sh --issue -d your.dedyn.io --keylength ec-384 -w /var/www/letsencrypt --key-file /etc/letsencrypt/ecc-certs/privkey.pem --ca-file /etc/letsencrypt/ecc-certs/chain.pem --cert-file /etc/letsencrypt/ecc-certs/cert.pem --fullchain-file /etc/letsencrypt/ecc-certs/fullchain.pem
exit
vi /root/permissions.sh
Paste the following rows:
#!/bin/bash
find /var/www/ -type f -print0 | xargs -0 chmod 0640
find /var/www/ -type d -print0 | xargs -0 chmod 0750
chown -R www-data:www-data /var/www/
chown -R www-data:www-data /var/nc_data/
chmod -R 775 /var/www/letsencrypt
chmod -R 755 /etc/letsencrypt
chmod 0644 /var/www/nextcloud/.htaccess
chmod 0644 /var/www/nextcloud/.user.ini
chmod 600 /etc/letsencrypt/rsa-certs/fullchain.pem
chmod 600 /etc/letsencrypt/rsa-certs/privkey.pem
chmod 600 /etc/letsencrypt/rsa-certs/chain.pem
chmod 600 /etc/letsencrypt/rsa-certs/cert.pem
chmod 600 /etc/letsencrypt/ecc-certs/fullchain.pem
chmod 600 /etc/letsencrypt/ecc-certs/privkey.pem
chmod 600 /etc/letsencrypt/ecc-certs/chain.pem
chmod 600 /etc/letsencrypt/ecc-certs/cert.pem
chmod 600 /etc/ssl/certs/dhparam.pem
exit 0
Run the script:
chmod +x /root/permissions.sh && /root/permissions.sh
sed -i '/ssl-cert-snakeoil/d' /etc/nginx/ssl.conf
sed -i s/\#\ssl/\ssl/g /etc/nginx/ssl.conf
service nginx restart
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ maintenance:install --database "mysql" --database-name "nextcloud" --database-user "nextcloud" --database-pass "nextcloud" --admin-user "YourNextcloudAdmin" --admin-pass "YourNextcloudAdminPasssword" --data-dir "/var/nc_data"'
Information
–database-name “nextcloud” : As set above while creating the database
–database-user “nextcloud” : As set above while creating the database user
–database-pass “nextcloud” : As set above while creating the user password
–admin-user “YourNextcloudAdmin” : your free choice
–admin-pass “YourNextcloudAdminPasssword” : your free choice
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ config:system:set trusted_domains 0 --value=your.dedyn.io'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ config:system:set overwrite.cli.url --value=https://your.dedyn.io'
cp /var/www/nextcloud/config/config.php /var/www/nextcloud/config/config.php.bak
Expand your Nextcloud config.php:
sed -i 's/^[ ]*//' /var/www/nextcloud/config/config.php
sed -i '/);/d' /var/www/nextcloud/config/config.php
cat <<EOF >>/var/www/nextcloud/config/config.php
'activity_expire_days' => 14,
'auth.bruteforce.protection.enabled' => true,
'blacklisted_files' =>
array (
0 => '.htaccess',
1 => 'Thumbs.db',
2 => 'thumbs.db',
),
'cron_log' => true,
'enable_previews' => true,
'enabledPreviewProviders' =>
array (
0 => 'OC\\Preview\\PNG',
1 => 'OC\\Preview\\JPEG',
2 => 'OC\\Preview\\GIF',
3 => 'OC\\Preview\\BMP',
4 => 'OC\\Preview\\XBitmap',
5 => 'OC\\Preview\\Movie',
6 => 'OC\\Preview\\PDF',
7 => 'OC\\Preview\\MP3',
8 => 'OC\\Preview\\TXT',
9 => 'OC\\Preview\\MarkDown',
),
'filesystem_check_changes' => 0,
'filelocking.enabled' => 'true',
'htaccess.RewriteBase' => '/',
'integrity.check.disabled' => false,
'knowledgebaseenabled' => false,
'logfile' => '/var/nc_data/nextcloud.log',
'loglevel' => 2,
'logtimezone' => 'Europe/Berlin',
'log_rotate_size' => 104857600,
'maintenance' => false,
'memcache.local' => '\\OC\\Memcache\\APCu',
'memcache.locking' => '\\OC\\Memcache\\Redis',
'overwriteprotocol' => 'https',
'preview_max_x' => 1024,
'preview_max_y' => 768,
'preview_max_scale_factor' => 1,
'redis' =>
array (
'host' => '/var/run/redis/redis-server.sock',
# ATTENTION if you operate on Debian 9.x:
# 'host' => '/var/run/redis/redis.sock',
'port' => 0,
'timeout' => 0.0,
),
'quota_include_external_storage' => false,
'share_folder' => '/Shares',
'skeletondirectory' => '',
'theme' => '',
'trashbin_retention_obligation' => 'auto, 7',
'updater.release.channel' => 'stable',
);
EOF
sudo -u www-data sed -i "s/.*dbhost.*/\'dbhost\' \=\>\ \'localhost\:\/var\/run\/mysqld\/mysqld\.sock\'\,/g" /var/www/nextcloud/config/config.php
sudo -u www-data sed -i "s/output_buffering=.*/output_buffering='Off'/" /var/www/nextcloud/.user.ini
service php7.3-fpm restart && service redis-server restart && service nginx restart
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ app:disable survey_client'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ app:disable firstrunwizard'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ app:enable admin_audit'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ app:enable files_pdfviewer'
(a) update your environment periodically
vi /root/upgrade.sh
#!/bin/bash
/usr/sbin/service nginx stop
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/updater/updater.phar'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ status'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ -V'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ db:add-missing-indices'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ db:convert-filecache-bigint'
sed -i "s/output_buffering=.*/output_buffering='Off'/" /var/www/nextcloud/.user.ini
chown -R www-data:www-data /var/www/nextcloud
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ update:check'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ app:update --all'
/usr/sbin/service php7.3-fpm restart
/usr/sbin/service nginx restart
exit 0
(infos: BigInt, missing indices)
(b) optimize your Nextcloud periodically
vi /root/optimize.sh
#!/bin/bash
# ATTENTION if you operate on Debian 9.x:
# redis-cli -s /var/run/redis/redis.sock <<EOF
redis-cli -s /var/run/redis/redis-server.sock <<EOF
FLUSHALL
quit
EOF
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ files:scan --all'
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ files:scan-app-data'
exit 0
Save both scripts and mark them as executable
chmod +x /root/*.sh
Issue both once:
/root/upgrade.sh && /root/optimize.sh
For www-data:
crontab -u www-data -e
Paste the following rows
*/5 * * * * php -f /var/www/nextcloud/cron.php > /dev/null 2>&1
For root:
crontab -e
Paste the follwoing rows:
5 1 * * * /root/optimize.sh 2>&1
su - www-data -s /bin/bash -c 'php /var/www/nextcloud/occ background:cron'
service mysql restart && service php7.3-fpm restart && service redis-server restart && service nginx restart
https://your.dedyn.io/login
If the integrity check within Nextcloud will fail, try to change the config.php
sudo -u www-data sed -i "s/.*integrity.check.disabled.*/'integrity.check.disabled' => true,/g" /var/www/nextcloud/config/config.php
Re-run the integrity check and set the value back to ‘false’:
sudo -u www-data sed -i "s/.*integrity.check.disabled.*/'integrity.check.disabled' => false,/g" /var/www/nextcloud/config/config.php
Refresh the adminpanel (F5) and the message should disappear!
First install and configure fail2ban and finally configure the firewall ufw to secure and harden Nextcloud.
apt update && apt install fail2ban -y
Paste the following rows to the fail2ban filter for Nextcloud:
vi /etc/fail2ban/filter.d/nextcloud.conf
[Definition]
failregex=^{"reqId":".*","remoteAddr":".*","app":"core","message":"Login failed: '.*' \(Remote IP: '<HOST>'\)","level":2,"time":".*"}$
^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","user,:".*","app":"no app in context".*","method":".*","message":"Login failed: '.*' \(Remote IP: '<HOST>'\)".*}$
^{"reqId":".*","level":2,"time":".*","remoteAddr":".*","user":".*","app":".*","method":".*","url":".*","message":"Login failed: .* \(Remote IP: <HOST>\).*}$
Paste the following rows to the fail2ban jail for Nextcloud:
vi /etc/fail2ban/jail.d/nextcloud.local
[nextcloud]
backend = auto
enabled = true
port = 80,443
protocol = tcp
filter = nextcloud
maxretry = 3
bantime = 36000
findtime = 36000
logpath = /var/nc_data/nextcloud.log
[nginx-http-auth]
enabled = true
service fail2ban restart && fail2ban-client status nextcloud
apt install ufw -y
ufw allow 80/tcp && ufw allow 443/tcp && ufw allow 22/tcp && ufw logging medium
ufw default deny incoming && ufw enable && service ufw restart
If you’d like to prevent “unprivileged visitors” just create the script /root/ufw-spamhaus.sh and block them by ufw directly.
vi /root/ufw-spamhaus.sh
Paste the following rows:
#!/bin/bash
# Thanks to @ank0m
EXEC_DATE=`date +%Y-%m-%d`
SPAMHAUS_DROP="/usr/local/src/drop.txt"
SPAMHAUS_eDROP="/usr/local/src/edrop.txt"
URL="https://www.spamhaus.org/drop/drop.txt"
eURL="https://www.spamhaus.org/drop/edrop.txt"
DROP_ADD_TO_UFW="/usr/local/src/DROP2.txt"
eDROP_ADD_TO_UFW="/usr/local/src/eDROP2.txt"
DROP_ARCHIVE_FILE="/usr/local/src/DROP_$EXEC_DATE"
eDROP_ARCHIVE_FILE="/usr/local/src/eDROP_$EXEC_DATE"
# All credits for the following BLACKLISTS goes to "The Spamhaus Project" - https://www.spamhaus.org
echo "Start time: $(date)"
echo " "
echo "Download daily DROP file:"
wget -q -O - "$URL" > $SPAMHAUS_DROP
grep -v '^;' $SPAMHAUS_DROP | cut -d ' ' -f 1 > $DROP_ADD_TO_UFW
echo " "
echo "Extract DROP IP addresses and add to UFW:"
cat $DROP_ADD_TO_UFW | while read line
do
/usr/sbin/ufw insert 1 deny from "$line" comment 'DROP_Blacklisted_IPs'
done
echo " "
echo "Downloading eDROP list and import to UFW"
echo " "
echo "Download daily eDROP file:"
wget -q -O - "$eURL" > $SPAMHAUS_eDROP
grep -v '^;' $SPAMHAUS_eDROP | cut -d ' ' -f 1 > $eDROP_ADD_TO_UFW
echo " "
echo "Extract eDROP IP addresses and add to UFW:"
cat $eDROP_ADD_TO_UFW | while read line
do
/usr/sbin/ufw insert 1 deny from "$line" comment 'eDROP_Blacklisted_IPs'
done
echo " "
#####
## To remove or revert these rules, keep the list of IPs!
## Run a command like so to remove the rules:
# while read line; do ufw delete deny from $line; done < $ARCHIVE_FILE
#####
echo "Backup DROP IP address list:"
mv $DROP_ADD_TO_UFW $DROP_ARCHIVE_FILE
echo " "
echo "Backup eDROP IP address list:"
mv $eDROP_ADD_TO_UFW $eDROP_ARCHIVE_FILE
echo " "
echo End time: $(date)
Make the script exutable by issuing
chmod +x /root/ufw-spamhaus.sh
and configure it in your crontab to be issued automatically.
(crontab -l ; echo "10 2 * * * /root/ufw-spamhaus.sh 2>&1") | crontab -u root -
Finally perform an initial run
/root/ufw-spamhaus.sh
and many UFW rules will be applied immediately. Be patient, it may take a while.
Start download netdata – the directory ‘netdata’ will be created
apt install apache2-utils git gcc make autoconf automake pkg-config uuid-dev zlib1g-dev
cd /usr/local/src
git clone https://github.com/firehol/netdata.git --depth=1
cd netdata
Create a passwordfile to protect netdata:
htpasswd -c /etc/nginx/netdata-access YourName
Then run the script netdata-installer.sh with root privileges to build, install and start netdata
./netdata-installer.sh
Netdata is already installed. We will make smaller adjustementss to netdata’s configuration:
vi /etc/netdata/netdata.conf
First we change the value for “history” to e.g. 14400 (4 hours of chart data retention, uses about 60 MB of RAM) in the [global] section:
history = 14400
Then we change the binding in the [web] section to localhost (127.0.0.1) only:
bind to = 127.0.0.1
Finally we enhance the nextcloud.conf and nginx.conf file to include the netdata webserver-configuration:
vi /etc/nginx/conf.d/nextcloud.conf
Paste the red rows as shown below to the nextcloud.conf:
...
location / {
rewrite ^ /index.php$request_uri;
}
location /netdata {
return 301 /netdata/;
}
location ~ /netdata/(?<ndpath>.*) {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/netdata-access;
proxy_http_version 1.1;
proxy_pass_request_headers on;
proxy_set_header Connection "keep-alive";
proxy_store off;
proxy_pass http://netdata/$ndpath$is_args$args;
gzip on;
gzip_proxied any;
gzip_types *;
}
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)/ {
deny all;
...
Create the new /etc/nginx/conf.d/stub_status.conf:
vi /etc/nginx/conf.d/stub_status.conf
Paste all the following rows:
server {
listen 127.0.0.1:80 default_server;
server_name 127.0.0.1;
location /stub_status {
stub_status on;
allow 127.0.0.1;
deny all;
}
}
Save and quit the file (:wq!) and modify the file /etc/nginx/nginx.conf:
...
http {
server_names_hash_bucket_size 64;
upstream php-handler {
server unix:/run/php/php7.3-fpm.sock;
}
upstream netdata {
server 127.0.0.1:19999;
keepalive 64;
}
...
Save and quit the file (:wq!) and check NGINX
nginx -t
If no errors appear just restart netdata and nginx
service netdata restart && service nginx restart
and call netdata in your browser
https://your.dedyn.io/netdata
or as an external site in your Nextcloud.
(a) …using a NAS
(b) …using an external/additional HDD
(c) … using Nextclouds external storage app
You may enhance your Nextcloud with data from your NAS-share or an external hdd.
It is really simple to mount a NAS share to your Nextcloud using cifs. First install cifs-utils:
apt install cifs-utils -y
Then store your credentials to a special file (e.g. /root/.smbcredentials)
vi /root/.smbcredentials
Write down your username and password:
username=NASuser
password=NASPassword
Save and quit (:wq!) the file and change the permissions to 0600:
chmod 400 /root/.smbcredentials
Detect the ID of the webuser (www-data) using the id-command:
id www-data
and keep the id in mind to reuse it in /etc/fstab:
cp /etc/fstab /etc/fstab.bak
vi /etc/fstab
Paste the following to the end of fstab
//<NAS>/<share> /var/nc_data/next/files cifs user,uid=33,rw,iocharset=utf8,suid,credentials=/root/.smbcredentials,file_mode=0770,dir_mode=0770 0 0
Please substitue “//<NAS>/<share>“, next and if neccessary the uid=”33” and then try to mount your NAS manually first:
mount //<NAS>/<share>/
or
mount -a
To unmount your NAS manually run
umount //<NAS>/<share>/
or
umount -a
It will be neccessary to rescan your data for the first usage once. So change to your Nextcloud directory and execute Nextclouds files:scan for the relevant Nextcloud-user (e.g. next) or all (–all):
service nginx stop
cd /var/www/nextcloud
redis-cli -s /var/run/redis/redis.sock
FLUSHALL
quit
sudo -u www-data php occ files:scan --all -v
sudo -u www-data php occ files:scan-app-data -v
service nginx start
After Nextclouds files:scan all of your NAS data will appear in the Nextcloud file-app.
The permissions-script <permission.sh> should be enhanced to umount and mount the new mounted NAS share:
vi /root/permissions.sh
Add the red lines to the existing script:
#!/bin/bash
find /var/www/ -type f -print0 | xargs -0 chmod 0640
find /var/www/ -type d -print0 | xargs -0 chmod 0750
chown -R www-data:www-data /var/www/
umount //<NAS>/<share>
chown -R www-data:www-data /var/nc_data/
mount //<NAS>/<share>
chmod 0644 /var/www/nextcloud/.htaccess
chmod 0644 /var/www/nextcloud/.user.ini
chmod 600 /etc/letsencrypt/rsa-certs/fullchain.pem
chmod 600 /etc/letsencrypt/rsa-certs/privkey.pem
chmod 600 /etc/letsencrypt/rsa-certs/chain.pem
chmod 600 /etc/letsencrypt/rsa-certs/cert.pem
chmod 600 /etc/letsencrypt/ecc-certs/fullchain.pem
chmod 600 /etc/letsencrypt/ecc-certs/privkey.pem
chmod 600 /etc/letsencrypt/ecc-certs/chain.pem
chmod 600 /etc/letsencrypt/ecc-certs/cert.pem
chmod 600 /etc/ssl/certs/dhparam.pem
Please substitute the red ones accordingly to your environment, then save and quit (:wq!) the file. From now, your NAS will always be available in Nextcloud for the specific user.
We prepare the new drive ‘/dev/sda‘ for the use in Nextcloud. Please format it with an ‘ext4’ file system and mount it permanently with an entry in /etc/fstab.
Stop your server (NGINX, PHP, MariaDB, Redis) services and check the availability of the new drive:
sudo -s
service nginx stop && service php7.3-fpm stop && service redis-server stop && service mysql stop
fdisk -l /dev/sda
If available, make a new partition with the fdisk command.
fdisk /dev/sda
1.Type ‘o’ to create a new partition table.
2.Type ‘n’ to create a new partition.
3.Choose the primary partition type, input ‘p’.
4.Partition Number – we just need 1.
5.Leave all default on the First sector and Last sector – Press Enter.
6.Type ‘w’ and press enter to write the partition.
The ‘/dev/sda1’ partition has been created, now we have to format it to ‘ext4’ with the mkfs tool. Then check the volume size.
mkfs.ext4 /dev/sda1
fdisk -s /dev/sda1
Next, create a new local ‘nc_data’ directory and mount ‘/dev/sda1’ to that directory.
sudo mkdir -p /nc_data
To mount new disk permanently, we add the new mount configuration to the fstab file. Open fstab with vom:
vi /etc/fstab
Paste the configuration below at the end of the file.
/dev/sda1 /nc_data ext4 defaults 0 1
Save fstab and exit:
Now mount the disk and make sure that there is no error.
mount -a
df -h
At least you have to move your current Nextcloud data direcory to the new mounted directory
chown -R www-data:www-data /nc_data
rsync -av /var/nc_data/ /nc_data
and point to it in Nextcloud’s config.php.
sudo -u www-data vi /var/www/nextcloud/config/config.php
Change the data-directory
...
'datadirectory' => '/nc_data',
...
Finally restart your server services and perform a new filescan:
service nginx stop && service php7.3-fpm restart && service redis-server restart && service mysql restart
cd /var/www/nextcloud
redis-cli -s /var/run/redis/redis.sock
FLUSHALL
quit
sudo -u www-data php occ files:scan --all -v
sudo -u www-data php occ files:scan-app-data -v
service nginx restart
From now, your Nextcloud data will be stored on your external HDD.
As an enhancement for (a) and (b) you can enable the external storage app and participate of many benefits:
•.files can be created, edited and deleted on both sides: witihin and outside Nextcloud
•.you are enabled to mount external storage services and devices as secondary Nextcloud storage devices
•.users are allowed to mount their own external storage services
In case you want to use Samba, issue the following statement
apt install php-smbclient smbclient -y
and restart PHP
service php7.3-fpm restart
(a) configure fail2ban system-notification mails
(b) install apticron and configure system update-notification mails
First install msmtp
sudo -s
apt update && apt upgrade -y && apt install msmtp msmtp-mta mailutils -y
Create your configuration: create both files and paste / amend the following rows:
vi /etc/msmtprc
and
vi ~/.msmtprc
defaults
port 587
tls on
tls_starttls on
tls_trust_file /etc/ssl/certs/ca-certificates.crt
#Your Mail:
account yourmail@domain.com
#Your SMTP-Server:
host smtp.domain.com
#Mails will be sent from:
from yourmail@domain.com
auth on
#Your Mailaccount:
user yourmail@domain.com
#Your Password:
password yOUr-S3CrET
#Default Mailaccount:
account default: yourmail@domain.com
aliases /etc/aliases
# find out more about the configuration here: https://marlam.de/msmtp/msmtprc.txt
Set the proper permission
chmod 600 /etc/msmtprc
chmod 600 ~/.msmtprc
and create the mail.rc file:
vi /etc/mail.rc
set sendmail="/usr/bin/msmtp -t"
Test your mailserver configuration by issuing
echo "Sending test mail..." | mail -s "Subject" yourmail@domain.com
Amend logrotate to handle msmtp properly. Create and edit the file
vi /etc/logrotate.d/msmtp
and paste all the following rows:
/var/log/msmtp/*.log {
rotate 12
monthly
compress
missingok
notifempty
}
Amend PHP to make use of msmtp within PHP
vi /etc/php/7.3/fpm/php.ini
Set the sendmail_path as follows
sendmail_path = "/usr/bin/msmtp -t"
and restart php
service php7.3-fpm restart
Finally define your mail aliases: open the file
vi /etc/aliases
and paste and amend the following rows:
root: yourmail@domain.com
default: yourmail@domain.com
From now your system is preparred to send mails. If you want fail2ban to keep you informed just follow the next chapter:
We substitute the root-User in the fail2ban-config to receive status mails of fail2ban in the future. Those mails will contain both, the fail2ban-status (stopped/started) and in case of failed logins also the banned ip(‘s). Edit the fail2ban configuration file
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.conf.bak
vi /etc/fail2ban/jail.conf
and substitute at least the red marked parameters according to your system:
...
destemail = yourmail@domain.com
...
sender = yourmail@domain.com
...
mta = mail
...
# action = %(action_)s
action = %(action_mwl)s
...
Save and quit (:wq!) the fail2ban configuration. To avoid (many) mails on every fail2ban-restart just create a new file and copy it as shown below:
vi /etc/fail2ban/action.d/mail-buffered.local
Paste the following rows
[Definition]
actionstart =
actionstop =
Copy the file
cp /etc/fail2ban/action.d/mail-buffered.local /etc/fail2ban/action.d/mail.local
cp /etc/fail2ban/action.d/mail-buffered.local /etc/fail2ban/action.d/mail-whois-lines.local
cp /etc/fail2ban/action.d/mail-buffered.local /etc/fail2ban/action.d/mail-whois.local
cp /etc/fail2ban/action.d/mail-buffered.local /etc/fail2ban/action.d/sendmail-buffered.local
cp /etc/fail2ban/action.d/mail-buffered.local /etc/fail2ban/action.d/sendmail-common.local
Re-start the fail2ban-service an you will (only) be informed if fail2ban blocked new IPs
service fail2ban restart
automatically.
If you use APTICRON, your system may send emails in case of available systemupdates either.
apt install apticron -y
After havin installed APTICRON you should edit the config and substitute at least your EMAIL, SYSTEM, NOTIFY_NO_UPDATES and CUSTOM_FROM.
cp /etc/apticron/apticron.conf /etc/apticron/apticron.conf.bak
vi /etc/apticron/apticron.conf
...
EMAIL="yourmail@domain.com"
...
SYSTEM="your.dedyn.io"
...
NOTIFY_HOLDS="1"
...
NOTIFY_NO_UPDATES="1"
...
CUSTOM_SUBJECT='$SYSTEM: $NUM_PACKAGES package update(s)'
...
CUSTOM_NO_UPDATES_SUBJECT='$SYSTEM: no updates available'
...
CUSTOM_FROM="yourmail@domain.com"
...
To run and check APTICRON just call
apticron
and you will receive an email sent by APTICRON. Now you are a little bit more secure.
cp /etc/cron.d/apticron /etc/cron.d/apticron.bak
vi /etc/cron.d/apticron
30 7 * * * root if test -x /usr/sbin/apticron; then /usr/sbin/apticron --cron; else true; fi
Apticron will now be executed by cron.d. You can change the starttime e.g. to daily 7.30 AM.
The following steps are system relevant (critical) and only recommended for advanced linux users. If the ssh configuration will fail, you won’t be able to login to your system via ssh anymore. The mandatory prerequisite is a ssh server that you can log on using private/public key only!
Install the software for 2FA (Two-Factor-Authentication) with your preferred OTP AUTH app
apt install libpam-google-authenticator -y
Leave the root-Shell and run the following command as your <your-ubuntu-user-name> and NOT as root:
exit
google-authenticator
You will be asked for:
Do you want authentication tokens to be time-based (y/n) y
Do you want me to update your "~/.google_authenticator" file (y/n) y
Do you want to disallow multiple uses of the same authentication
token? This restricts you to one login about every 30s, but it increases
your chances to notice or even prevent man-in-the-middle attacks (y/n) y
By default, tokens are good for 30 seconds and in order to compensate for
possible time-skew between the client and the server, we allow an extra
token before and after the current time. If you experience problems with poor
time synchronization, you can increase the window from its default
size of 1:30min to about 4min. Do you want to do so (y/n) n
If the computer that you are logging into isn't hardened against brute-force
login attempts, you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y
Change back to the root-Shell
sudo -s
Backup the current configuration and configure your ssh server
cp /etc/pam.d/sshd /etc/pam.d/sshd.bak
vi /etc/pam.d/sshd
Change the file to mine:
@include common-auth
@include common-password
auth required pam_google_authenticator.so
account required pam_nologin.so
@include common-account
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
session required pam_loginuid.so
session optional pam_keyinit.so force revoke
@include common-session
session optional pam_motd.so motd=/run/motd.dynamic
session optional pam_motd.so noupdate
session optional pam_mail.so standard noenv # [1]
session required pam_limits.so
session required pam_env.so # [1]
session required pam_env.so user_readenv=1 envfile=/etc/default/locale
session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
Save and quit (:wq!) the file.
If not already created please create your 4096 bit RSA Key (SSH) first:
cd ~
ssh-keygen -q -f /etc/ssh/ssh_host_rsa_key -N '' -b 4096 -t rsa
If you will be asked to overwrite the existing key, confirm with ‘Y’. Then backup, edit and change your SSH-config to examplarily mine
mv /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
vi /etc/ssh/sshd_config
# Port 22
Port 1234 #your decision, but keep UFW in mind!
Protocol 2
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
UsePrivilegeSeparation yes
KeyRegenerationInterval 3600
ServerKeyBits 4096
SyslogFacility AUTH
LogLevel INFO
LoginGraceTime 30s
PermitRootLogin no
StrictModes yes
RSAAuthentication yes
PubkeyAuthentication yes
IgnoreRhosts yes
UseDNS yes
RhostsRSAAuthentication no
HostbasedAuthentication no
IgnoreUserKnownHosts yes
PermitEmptyPasswords no
MaxAuthTries 3
MaxSessions 3
ChallengeResponseAuthentication yes
X11Forwarding yes
X11DisplayOffset 10
X11UseLocalhost no
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
Banner /etc/issue
AcceptEnv LANG LC_*
Subsystem sftp /usr/lib/openssh/sftp-server
UsePAM yes
AllowTcpForwarding no
AllowUsers ubuntuuser #<your-ubuntu-user-name> for e.g. putty or ssh native
AuthenticationMethods publickey,password publickey,keyboard-interactive
If you changed the ssh-Port to e.g. 1234, please ensure having changed your ufw-configuration either and adjust the username in ‘AllowUsers ubuntuuser‘.
Paste your public key to the <ubuntuuser>’s keystore (ubuntu’s how-to):
vi ~/.ssh/authorized_keys
and set proper permissions:
sudo chown -R ubuntuuser:ubuntuuser ~/.ssh
sudo chmod 700 ~/.ssh
sudo chmod 600 ~/.ssh/authorized_keys
Then restart your ssh server
service ssh restart
and re-logon to your server using a new session-window. This is your final fallback, if you misconfigured your ssh server ;-). From now your privat key is needed, you will be prompted for your password and finally for your new second factor.
Start your e.g. OTP AUTH or Google Authenticator app and read your second factor to gain access to your server.
You will be logged on using your second factor.
First install logwatch
apt update && apt install logwatch -y
then copy the default configuration files to the logwatch folder:
cp /usr/share/logwatch/default.conf/logfiles/http.conf /etc/logwatch/conf/logfiles/nginx.conf
cp /usr/share/logwatch/default.conf/services/http.conf /etc/logwatch/conf/services/nginx.conf
cp /usr/share/logwatch/scripts/services/http /usr/share/logwatch/scripts/services/nginx
cp /usr/share/logwatch/default.conf/services/http-error.conf /etc/logwatch/conf/services/nginx-error.conf
cp /usr/share/logwatch/scripts/services/http-error /etc/logwatch/scripts/services/nginx-error
cp /etc/logwatch/conf/logfiles/nginx.conf /etc/logwatch/conf/logfiles/nginx.conf.org.bak
Edit the /etc/logwatch/conf/logfiles/nginx.conf to mine
vi /etc/logwatch/conf/logfiles/nginx.conf
Substitute the entire file to:
########################################################
# Define log file group for NGINX
########################################################
# What actual file? Defaults to LogPath if not absolute path....
#LogFile = httpd/*access_log
#LogFile = apache/*access.log.1
#LogFile = apache/*access.log
#LogFile = apache2/*access.log.1
#LogFile = apache2/*access.log
#LogFile = apache2/*access_log
#LogFile = apache-ssl/*access.log.1
#LogFile = apache-ssl/*access.log
LogFile = nginx/*access.log
LogFile = nginx/*error.log
LogFile = nginx/*access.log.1
LogFile = nginx/*error.log.1
# If the archives are searched, here is one or more line
# (optionally containing wildcards) that tell where they are...
#If you use a "-" in naming add that as well -mgt
#Archive = archiv/httpd/*access_log.*
#Archive = httpd/*access_log.*
#Archive = apache/*access.log.*.gz
#Archive = apache2/*access.log.*.gz
#Archive = apache2/*access_log.*.gz
#Archive = apache-ssl/*access.log.*.gz
#Archive = archiv/httpd/*access_log-*
#Archive = httpd/*access_log-*
#Archive = apache/*access.log-*.gz
#Archive = apache2/*access.log-*.gz
#Archive = apache2/*access_log-*.gz
#Archive = apache-ssl/*access.log-*.gz
Archive = nginx/*access.log.*.gz
Archive = nginx/*error.log.*.gz
# Expand the repeats (actually just removes them now)
*ExpandRepeats
# Keep only the lines in the proper date range...
*ApplyhttpDate
# vi: shiftwidth=3 tabstop=3 et
Save and quit (:wq!) this file and edit /etc/logwatch/conf/services/nginx.conf:
cp /etc/logwatch/conf/services/nginx.conf /etc/logwatch/conf/services/nginx.conf.org.bak
vi /etc/logwatch/conf/services/nginx.conf
Change the name from http to NGINX or substitute the entire file to mine:
###########################################################################
# Configuration file for NGINX filter
###########################################################################
Title = "NGINX"
# Which logfile group...
LogFile = NGINX
# Define the log file format
#
# This is now the same as the LogFormat parameter in the configuration file
# for httpd. Multiple instances of declared LogFormats in the httpd
# configuration file can be declared here by concatenating them with the
# '|' character. The default, shown below, includes the Combined Log Format,
# the Common Log Format, and the default SSL log format.
#$LogFormat = "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"|%h %l %u %t \"%r\" %>s %b|%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
# The following is supported for backwards compatibility, but deprecated:
# Define the log file format
#
# the only currently supported fields are:
# client_ip
# request
# http_rc
# bytes_transfered
# agent
#
#$HTTP_FIELDS = "client_ip ident userid timestamp request http_rc bytes_transfered referrer agent"
#$HTTP_FORMAT = "space space space brace quote space space quote quote"
# Define the field formats
#
# the only currently supported formats are:
# space = space delimited field
# quote = quoted ("..") space delimited field
# brace = braced ([..]) space delimited field
# Flag to ignore 4xx and 5xx error messages as possible hack attempts
#
# Set flag to 1 to enable ignore
# or set to 0 to disable
$HTTP_IGNORE_ERROR_HACKS = 0
# Ignore requests
# Note - will not do ANY processing, counts, etc... just skip it and go to
# the next entry in the log file.
# Note - The match will be case insensitive; e.g. /model/ == /MoDel/
# Examples:
# 1. Ignore all URLs starting with /model/ and ending with 1 to 10 digits
# $HTTP_IGNORE_URLS = ^/model/\d{1,10}$
#
# 2. Ignore all URLs starting with /model/ and ending with 1 to 10 digits and
# all URLS starting with /photographer and ending with 1 to 10 digits
# $HTTP_IGNORE_URLS = ^/model/\d{1,10}$|^/photographer/\d{1,10}$
# or simply:
# $HTTP_IGNORE_URLS = ^/(model|photographer)/\d{1,10}$
# To ignore a range of IP addresses completely from the log analysis,
# set $HTTP_IGNORE_IPS. For example, to ignore all local IP addresses:
#
# $HTTP_IGNORE_IPS = ^10\.|^172\.(1[6-9]|2[0-9]|3[01])\.|^192\.168\.|^127\.
#
# For more sophisticated ignore rules, you can define HTTP_IGNORE_EVAL
# to an arbitrary chunk of code.
# The default is not to filter anything:
$HTTP_IGNORE_EVAL = 0
# Example:
# $HTTP_IGNORE_EVAL = "($field{http_rc} == 401) && ($field{client_ip}=~/^192\.168\./) && ($field{url}=~m%^/protected1/%)"
# See the "scripts/services/http" script for other variables that can be tested.
# The variable $HTTP_USER_DISPLAY defines which user accesses are displayed.
# The default is not to display user accesses:
$HTTP_USER_DISPLAY = 0
# To display access failures:
# $HTTP_USER_DISPLAY = "$field{http_rc} >= 400"
# To display all user accesses except "Unauthorized":
# $HTTP_USER_DISPLAY = "$field{http_rc} != 401"
# To raise the needed level of detail for one or more specific
# error codes to display a summary instead of listing each
# occurrence, set a variable like the following ones:
# Raise 403 codes to detail level High
#$http_rc_detail_rep_403 = 10
# Always show only summary for 404 codes
#$http_rc_detail_rep_404 = 20
# vi: shiftwidth=3 tabstop=3 et
Save and quit the file (:wq!) and disable the default apache-configuration files:
cd /usr/share/logwatch/default.conf/services
mv http-error.conf http-error.conf.bak && mv http.conf http.conf.bak
At least we create a cronjob to send the result from logwatch automatically:
crontab -e
Paste the following row:
@daily /usr/sbin/logwatch --output mail --mailto your@mail.com --format html --detail high --range yesterday > /dev/null 2>&1
Save and quit crontab and check if logwatch is configured properly:
/usr/sbin/logwatch --output mail --mailto your@mail.com --format html --detail high --range yesterday
You should receive an email from logwatch that looks like this:
From now you will receive daily mails containing your system summary.
Enjoy your personal data in your secured and hardened Nextcloud-Server!
Find more instructions here: Nextcloud backup and restore
Carsten Rieger
Carsten Rieger is a senior system engineer in full-time and also working as an IT freelancer. He is working with linux environments for more than 15 years, an Open Source enthusiast and highly motivated on linux installation and troubleshooting. Mostly working with Debian/Ubuntu Linux, Nginx and Apache web server, MariaDB/MySQL/PostgreSQL, PHP, Cloud infrastructure (e.g. Nextcloud) and other open source projects (e.g. Roundcube) and in voluntary work for the Dr. Michael & Angela Jacobi Stiftung for more than 7 years.